#include "gcpch.h"
#include "virtdir_processor.h"
#include "diritem_pool.h"
#include "gui_listpane.h"
#include "hsieh_hashfn.h"
#include "attributes.h"

struct geek_diritem_name_hsieh_hashfn
{
    static int calc(const geek_diritem *item)
    {
        return (int)geek_hsieh_hashfn((const char *)item->display_name,sizeof(item->display_name[0])*wcslen(item->display_name));
    }

    static int compare(const geek_diritem *l, const geek_diritem *r)
    {
        return wcscmp(l->display_name,r->display_name);
    }
};

static int tree_compare_fn(const geek_diritem *l, const geek_diritem *r)
{
    if((l->display_name[0] == L'.') != (r->display_name[0] == L'.'))
    {
        return l->display_name[0] == L'.' ? -1 : 1;
    }

    if((l->attributes & geek_attr_directory) != (r->attributes & geek_attr_directory))
    {
        return l->attributes & geek_attr_directory ? -1 : 1;
    }

    return wcscmp(l->display_name,r->display_name);
}

bool geek_virtdir_processor_create(geek_virtdir_processor *p)
{
    memset(p,0,sizeof(geek_virtdir_processor));
    geek_hash_create(&p->name_hash);
    geek_tree_create(&p->sort_tree);

    p->sort_tree.compare_fn = tree_compare_fn;

    return true;
}

void geek_virtdir_processor_destroy(geek_virtdir_processor *p)
{
    geek_tree_destroy(&p->sort_tree);
    geek_hash_destroy(&p->name_hash);
    memset(p,0,sizeof(geek_virtdir_processor));
}

void geek_virtdir_processor_on_new_diritem(geek_virtdir_processor *p, geek_diritem *item)
{
    geek_hash_insert(&p->name_hash,item);
    geek_tree_insert(&p->sort_tree,item);
}

void geek_virtdir_processor_on_update_diritem(geek_virtdir_processor *p, geek_diritem *item)
{
    geek_diritem *citem = geek_hash_find(&p->name_hash,item);
    if(citem != NULL)
    {
        geek_diritem_assign(item,citem);
    }
    else
    {
        geek_virtdir_processor_on_new_diritem(p,item);
    }
}

void geek_virtdir_processor_on_delete_diritem(geek_virtdir_processor *p, geek_diritem *item)
{
    geek_hash_remove(&p->name_hash,item);
    geek_tree_remove(&p->sort_tree,item);
}

void geek_virtdir_processor_on_begin_dirwalk(geek_virtdir_processor *p)
{
    geek_hash_clear(&p->name_hash);
    geek_tree_clear(&p->sort_tree);
    p->is_dirwalking = true;
}

void geek_virtdir_processor_on_end_dirwalk(geek_virtdir_processor *p)
{
    p->is_dirwalking = false;

    geek_gui_listpane_refresh(p->listpane);
}

void geek_virtdir_processor_on_cancel_dirwalk(geek_virtdir_processor *p)
{
    geek_hash_clear(&p->name_hash);
    geek_tree_clear(&p->sort_tree);

    p->is_dirwalking = false;
}

int geek_virtdir_processor_get_items(
    geek_virtdir_processor *p,
    int start,
    int count,
    geek_diritem *buffer
    )
{
    typedef geek_diritem item_t;
    typedef geek_treenode<item_t> node_t;

    geek_diritem *citem = geek_tree_nth_item(&p->sort_tree,start);
    if(citem == NULL)
    {
        return 0;
    }

    int itemnum = 0;
    while(itemnum < count && citem != NULL)
    {
        buffer[itemnum++] = *citem;

        node_t *cnode = p->sort_tree.get_treenode(citem);
        if(cnode->right != NULL)
        {
            citem = cnode->right;
            while(p->sort_tree.get_treenode(citem)->left != NULL)
            {
                citem = p->sort_tree.get_treenode(citem)->left;
            }
        }
        else if(cnode->parent != NULL)
        {
            node_t *pnode = p->sort_tree.get_treenode(cnode->parent);
            while(pnode->right == citem)
            {
                citem = cnode->parent;
                cnode = pnode;

                pnode = p->sort_tree.get_treenode(cnode->parent);
                if(pnode == NULL)
                {
                    return itemnum;
                }
            }

            citem = cnode->parent;
            cnode = pnode;
        }
        else
        {
            citem = NULL;
        }
    }

    return itemnum;
}